/*
 *  DelegationPattern.h
 *  AMPlugin
 *
 *  Created by Vu Tiet on 7/9/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 *  References: http://svolkov.blogspot.com/2009/01/delegation-pattern-in-c-my-recipe.html
 */


#ifndef _DELEGATION_PATTERN_INTERFACES_SVOLKOV_  
#define _DELEGATION_PATTERN_INTERFACES_SVOLKOV_  


#include <vector>  
#include <algorithm>  
class IDelegate;  


///Simple notifier interface.  
class INotifier  
    {  
    public:  
		void Subscribe(IDelegate* delegate) { _delegates.push_back(delegate);};  
		void Unsubscribe(IDelegate* delegate) { _delegates.erase(std::find(_delegates.begin(), _delegates.end(), delegate));};  
		
    protected:  
		std::vector<IDelegate*> _delegates;  
    };  


/** Subscriber interface. 
 @remarks  
 Any class can become subscriber to register own methods as callbacks. 
 */  
template <class T>  
class ISubscriber  
    {  
    public:  
		///Destructor.  
		virtual ~ISubscriber() = 0;  
		///Accepted arguments list for target callback method.  
		typedef void (T::*CallbackMethod) (int);  
		
		/** Register new delegate. 
		 @remarks Actually any other arguments can be added here to distinguish event type to subscribe etc. 
		 */  
		void Subscribe(INotifier* notifier, typename ISubscriber<T>::CallbackMethod method);  
		/** Unregister delegate. 
		 */  
		void Unsubscribe(INotifier* notifier, typename ISubscriber<T>::CallbackMethod method);  
		
    protected:  
		std::vector<std::pair<INotifier*, IDelegate*> > _delegates;  
    };  


/** Delegate object interface. 
 */  
class IDelegate  
    {  
    public:  
		///Call.  
		virtual void Invoke(int) = 0;  
		//virtual void Invoke() = 0;
		//virtual std::string GetNotificationString() = 0;
    };  


/** Delegate object with callback method pointer. 
 */  
template <class T>  
class MethodDelegate : public IDelegate  
    {  
		template <typename U> friend class ISubscriber;  
    protected:  
		T* _object;  
		typename ISubscriber<T>::CallbackMethod _method; 
    public:  
		///Constructor.  
		MethodDelegate(T* object, typename ISubscriber<T>::CallbackMethod method)  
		: _object(object), _method(method) {};  
		///Call.  
		void Invoke(int);
		//void Invoke();
    };  


template<class T>  
void MethodDelegate<T>::Invoke(int i)  
{  
	(_object->*_method)(i);  
}  

/*
template<class T>  
void MethodDelegate<T>::Invoke()  
{  
	(_object->*_method)();  
}  
*/


template <class T>  
ISubscriber<T>::~ISubscriber<T>()  
{  
	std::vector<std::pair<INotifier*, IDelegate*> >::iterator i = _delegates.begin(), iEnd = _delegates.end();  
	for (; i != iEnd; i++)  
	{  
		i->first->Unsubscribe(i->second);  
		delete(i->second);  
	}  
}  


template <class T>  
void ISubscriber<T>::Subscribe(INotifier* notifier, typename ISubscriber<T>::CallbackMethod method)  
{  
	std::vector<std::pair<INotifier*, IDelegate*> >::iterator i = _delegates.begin(), iEnd = _delegates.end();  
	for (; i != iEnd; i++)  
	{  
		if (i->first == notifier && ((MethodDelegate<T>*)i->second)->_method == method)  
			return;//already subscribed  
	}  
	IDelegate* delegate = new MethodDelegate<T>((T*)this, method);  
	_delegates.push_back(std::make_pair(notifier, delegate));  
	notifier->Subscribe(delegate);  
}  


template <class T>  
void ISubscriber<T>::Unsubscribe(INotifier* notifier, typename ISubscriber<T>::CallbackMethod method)  
{  
	std::vector<std::pair<INotifier*, IDelegate*> >::iterator i = _delegates.begin(), iEnd = _delegates.end();  
	for (; i != iEnd; i++)  
	{  
		if (i->first == notifier && ((MethodDelegate<T>*)i->second)->_method == method)  
		{  
			i->first->Unsubscribe(i->second);  
			delete i->second;  
			_delegates.erase(i);  
			return;  
		}  
	}  
}  


#endif  